﻿using System;
using System.Collections.Generic;
using System.Linq;
using NHibernate.Linq;
using Xenta.Api;
using Xenta.Entities;
using Xenta.Enums;
using Xenta.Security;
using Xenta.Utils;

namespace Xenta.Operations
{
    #region __General__

    /// <summary>
    /// Creates a localization profile.
    /// </summary>
    public class CreateLocalizationProfile : CreateAuditable<LocalizationProfileEntity>, 
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("GLBLZ");
            }
        }

        /// <summary>
        /// Gets or sets the code.
        /// </summary>
        public string Code
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        public string Name
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the language identifier.
        /// </summary>
        public int LanguageID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the time zone identifier.
        /// </summary>
        public int TimeZoneID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the currency identifier.
        /// </summary>
        public int CurrencyID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the country identifier.
        /// </summary>
        public int CountryID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the region identifier.
        /// </summary>
        public int RegionID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the dimension unity identifier.
        /// </summary>
        public int DimensionUnitID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the weight unity identifier.
        /// </summary>
        public int WeightUnitID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the information unity identifier.
        /// </summary>
        public int InformationUnitID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flags.
        /// </summary>
        public LocalizationProfileFlags Flags
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override Err Validate(IUnitOfWork uow)
        {
            if(String.IsNullOrWhiteSpace(Code))
                return Err.Of<LocalizationProfileEntity>().NotValid("Code");

            if(uow.Execute(new GetRegion
            {
                ThrowsException = true,
                EntityID = RegionID
            }).Result.Country.EntityID != CountryID)
                return Err.Of<RegionEntity>().NotIn("Country");

            if(!uow.Execute(new GetMeasureUnit
            {
                ThrowsException = true,
                EntityID = DimensionUnitID
            }).Result.Flags.HasFlag(MeasureUnitFlags.Dimension))
            {
                return Err.Of<LocalizationProfileEntity>().NotValid("DimensionUnit");
            }

            if(!uow.Execute(new GetMeasureUnit
            {
                ThrowsException = true,
                EntityID = WeightUnitID
            }).Result.Flags.HasFlag(MeasureUnitFlags.Weight))
            {
                return Err.Of<LocalizationProfileEntity>().NotValid("WeightUnit");
            }

            if(!uow.Execute(new GetMeasureUnit
            {
                ThrowsException = true,
                EntityID = InformationUnitID
            }).Result.Flags.HasFlag(MeasureUnitFlags.Information))
            {
                return Err.Of<LocalizationProfileEntity>().NotValid("InformationUnit");
            }

            return base.Validate(uow);
        }

        protected override void Construct(IUnitOfWork uow, LocalizationProfileEntity entity)
        {
            base.Construct(uow, entity);

            entity.Code = Code.TrimToLen(10);
            entity.Name = StringHelper.EnsureNotNull(Name).TrimToLen(100);
            entity.Language = uow.Execute(new GetLanguage
            {
                ThrowsException = true,
                EntityID = LanguageID
            }).Result;
            entity.TimeZone = uow.Execute(new GetTimeZone
            {
                ThrowsException = true,
                EntityID = TimeZoneID
            }).Result;
            entity.Currency = uow.Execute(new GetCurrency
            {
                ThrowsException = true,
                EntityID = CurrencyID
            }).Result;
            entity.Country = uow.Execute(new GetCountry
            {
                ThrowsException = true,
                EntityID = CountryID
            }).Result;
            entity.Region = uow.Execute(new GetRegion
            {
                ThrowsException = true,
                EntityID = RegionID
            }).Result;
            entity.DimensionUnit = uow.Execute(new GetMeasureUnit
            {
                ThrowsException = true,
                EntityID = DimensionUnitID
            }).Result;
            entity.WeightUnit = uow.Execute(new GetMeasureUnit
            {
                ThrowsException = true,
                EntityID = WeightUnitID
            }).Result;
            entity.InformationUnit = uow.Execute(new GetMeasureUnit
            {
                ThrowsException = true,
                EntityID = InformationUnitID
            }).Result;
            entity.Flags = Flags;
        }

        #endregion
    }

    /// <summary>
    /// Updates the localization profile.
    /// </summary>
    public class UpdateLocalizationProfile : UpdateAuditable<LocalizationProfileEntity>, 
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("GLBLZ");
            }
        }

        /// <summary>
        /// Gets or sets the code.
        /// </summary>
        public string Code
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        public string Name
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the language identifier.
        /// </summary>
        public int LanguageID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the time zone identifier.
        /// </summary>
        public int TimeZoneID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the currency identifier.
        /// </summary>
        public int CurrencyID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the country identifier.
        /// </summary>
        public int CountryID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the region identifier.
        /// </summary>
        public int RegionID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the dimension unity identifier.
        /// </summary>
        public int DimensionUnitID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the weight unity identifier.
        /// </summary>
        public int WeightUnitID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the information unity identifier.
        /// </summary>
        public int InformationUnitID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flags.
        /// </summary>
        public LocalizationProfileFlags Flags
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override Err Validate(IUnitOfWork uow)
        {
            if(String.IsNullOrWhiteSpace(Code))
                return Err.Of<LocalizationProfileEntity>().NotValid("Code");

            if(uow.Execute(new GetRegion
            {
                ThrowsException = true,
                EntityID = RegionID
            }).Result.Country.EntityID != CountryID)
                return Err.Of<RegionEntity>().NotIn("Country");

            if(!uow.Execute(new GetMeasureUnit
            {
                ThrowsException = true,
                EntityID = DimensionUnitID
            }).Result.Flags.HasFlag(MeasureUnitFlags.Dimension))
            {
                return Err.Of<LocalizationProfileEntity>().NotValid("DimensionUnit");
            }

            if(!uow.Execute(new GetMeasureUnit
            {
                ThrowsException = true,
                EntityID = WeightUnitID
            }).Result.Flags.HasFlag(MeasureUnitFlags.Weight))
            {
                return Err.Of<LocalizationProfileEntity>().NotValid("WeightUnit");
            }

            if(!uow.Execute(new GetMeasureUnit
            {
                ThrowsException = true,
                EntityID = InformationUnitID
            }).Result.Flags.HasFlag(MeasureUnitFlags.Information))
            {
                return Err.Of<LocalizationProfileEntity>().NotValid("InformationUnit");
            }

            return base.Validate(uow);
        }

        protected override void Revise(IUnitOfWork uow, LocalizationProfileEntity entity)
        {
            base.Revise(uow, entity);

            entity.Code = Code.TrimToLen(10);
            entity.Name = StringHelper.EnsureNotNull(Name).TrimToLen(100);
            entity.Language = uow.Execute(new GetLanguage
            {
                ThrowsException = true,
                EntityID = LanguageID
            }).Result;
            entity.TimeZone = uow.Execute(new GetTimeZone
            {
                ThrowsException = true,
                EntityID = TimeZoneID
            }).Result;
            entity.Currency = uow.Execute(new GetCurrency
            {
                ThrowsException = true,
                EntityID = CurrencyID
            }).Result;
            entity.Country = uow.Execute(new GetCountry
            {
                ThrowsException = true,
                EntityID = CountryID
            }).Result;
            entity.Region = uow.Execute(new GetRegion
            {
                ThrowsException = true,
                EntityID = RegionID
            }).Result;
            entity.DimensionUnit = uow.Execute(new GetMeasureUnit
            {
                ThrowsException = true,
                EntityID = DimensionUnitID
            }).Result;
            entity.WeightUnit = uow.Execute(new GetMeasureUnit
            {
                ThrowsException = true,
                EntityID = WeightUnitID
            }).Result;
            entity.InformationUnit = uow.Execute(new GetMeasureUnit
            {
                ThrowsException = true,
                EntityID = InformationUnitID
            }).Result;
            entity.Flags = Flags;
        }

        #endregion
    }

    /// <summary>
    /// Deletes the localization profile.
    /// </summary>
    public class DeleteLocalizationProfile : DeleteEntity<LocalizationProfileEntity>, 
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("GLBLZ");
            }
        }

        #endregion
    }

    /// <summary>
    /// Gets the localization profile by identifier.
    /// </summary>
    public class GetLocalizationProfile : QueryEntityByID<LocalizationProfileEntity>,
        IApiOperation
    {
        #region Options

        protected override bool Cacheable
        {
            get
            {
                return true;
            }
        }

        #endregion

        #region Utilities

        protected override IQueryable<LocalizationProfileEntity> Build(IUnitOfWork uow, IQueryable<LocalizationProfileEntity> query)
        {
            query = base.Build(uow, query);
            if(Fetch > FetchLevel.None)
            {
                query = query.Fetch(x => x.Country)
                    .Fetch(x => x.Region)
                    .Fetch(x => x.Language)
                    .Fetch(x => x.Currency)
                    .Fetch(x => x.TimeZone)
                    .Fetch(x => x.DimensionUnit)
                    .Fetch(x => x.WeightUnit)
                    .Fetch(x => x.InformationUnit);
            }
            return query;
        }

        #endregion
    }

    /// <summary>
    /// Gets the localization profile by code.
    /// </summary>
    public class GetLocalizationProfileByCode : QueryEntity<LocalizationProfileEntity>,
        IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets or sets the profile code.
        /// </summary>
        public string Code
        {
            get;
            set;
        }

        #endregion

        #region Options

        protected override bool Cacheable
        {
            get
            {
                return true;
            }
        }

        #endregion

        #region Utilities

        protected override IQueryable<LocalizationProfileEntity> Build(IUnitOfWork uow, IQueryable<LocalizationProfileEntity> query)
        {
            string code = StringHelper.EnsureNotNull(Code).TrimToLen(10);
            query = base.Build(uow, query);
            query = query.Where(x => x.Code.Equals(code));
            if(Fetch > FetchLevel.None)
            {
                query = query.Fetch(x => x.Country)
                    .Fetch(x => x.Region)
                    .Fetch(x => x.Language)
                    .Fetch(x => x.Currency)
                    .Fetch(x => x.TimeZone)
                    .Fetch(x => x.DimensionUnit)
                    .Fetch(x => x.WeightUnit)
                    .Fetch(x => x.InformationUnit);
            }
            return query;
        }

        #endregion
    }

    #endregion

    #region __Search__

    /// <summary>
    /// Search over localization profile collection.
    /// </summary>
    public class SearchLocalizationProfiles : SearchOperation<LocalizationProfileEntity>,
        IApiOperation
    {
        #region Ctors

        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        public SearchLocalizationProfiles()
        {
            SortDir = SortDirection.Ascending;
            SortBy = new []
            {
                LocalizationProfileSortBy.ID
            };
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the flag mask.
        /// </summary>
        public LocalizationProfileFlags FlagMask
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the sort direction.
        /// </summary>
        public SortDirection SortDir
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the sort by.
        /// </summary>
        public LocalizationProfileSortBy[] SortBy
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override IQueryable<LocalizationProfileEntity> Build(IUnitOfWork uow, IQueryable<LocalizationProfileEntity> query)
        {
            query = base.Build(uow, query);
            query = query.Fetch(x => x.Country)
                .Fetch(x => x.Region)
                .Fetch(x => x.TimeZone)
                .Fetch(x => x.Currency)
                .Fetch(x => x.Language)
                .Fetch(x => x.DimensionUnit)
                .Fetch(x => x.WeightUnit)
                .Fetch(x => x.InformationUnit);
            query = query.FilterByFlags(x => (int)x.Flags, (int)FlagMask);
            if(SortBy != null)
            {
                for(int i = 0; i < SortBy.Length; i++)
                {
                    switch(SortBy[i])
                    {
                        case LocalizationProfileSortBy.Code:
                            query = query.OrderBy(x => x.Code, SortDir, i != 0);
                            break;
                        case LocalizationProfileSortBy.Name:
                            query = query.OrderBy(x => x.Name, SortDir, i != 0);
                            break;
                        case LocalizationProfileSortBy.CreatedOn:
                            query = query.OrderBy(x => x.CreatedOn, SortDir, i != 0);
                            break;
                        default:
                            query = query.OrderBy(x => x.EntityID, SortDir, i != 0);
                            break;
                    }
                }
            }
            return query;
        }

        #endregion
    }

    /// <summary>
    /// Gets the collection of profiles as a dictionary, where 
    /// key - profile identifier and value - profile code.
    /// </summary>
    public class GetLocalizationProfileDictionary : SearchOperation<LocalizationProfileEntity, Int32, String>,
        IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets or sets the flag mask.
        /// </summary>
        public LocalizationProfileFlags FlagMask
        {
            get;
            set;
        }

        #endregion

        #region Options

        protected override bool Cacheable
        {
            get
            {
                return true;
            }
        }

        #endregion

        #region Utilities

        protected override IQueryable<LocalizationProfileEntity> Build(IUnitOfWork uow, IQueryable<LocalizationProfileEntity> query)
        {
            return base.Build(uow, query)
                .FilterByFlags(x => (int)x.Flags, (int)FlagMask)
                .OrderBy(x => x.Code);
        }

        protected override IQueryable<KeyValuePair<Int32, String>> Map(IQueryable<LocalizationProfileEntity> query)
        {
            return query.Select(x => new KeyValuePair<Int32, String>(x.EntityID, x.Name));
        }

        #endregion
    }

    #endregion
}
